package eu.dominicum.ft.pd.boundary.ejb;

import eu.dominicum.ft.pd.Country;
import eu.dominicum.ft.pd.EStageResultType;
import eu.dominicum.ft.pd.FantasyScore;
import eu.dominicum.ft.pd.FantasyStage;
import eu.dominicum.ft.pd.FantasyTour;
import eu.dominicum.ft.pd.Location;
import eu.dominicum.ft.pd.Participant;
import eu.dominicum.ft.pd.Person;
import eu.dominicum.ft.pd.ProfessionalCyclist;
import eu.dominicum.ft.pd.Stage;
import eu.dominicum.ft.pd.StageResult;
import eu.dominicum.ft.pd.StageResultDetail;
import eu.dominicum.ft.pd.Team;
import eu.dominicum.ft.pd.TeamMember;
import eu.dominicum.ft.pd.boundary.FantasyTourManagement;
import eu.dominicum.ft.pd.mao.AssociateLocationsWithStageMAO;
import eu.dominicum.ft.pd.mao.AssociateTeamMemberByYerseyNumberMAO;
import eu.dominicum.ft.pd.mao.MakeFantasyTourMAO;
import eu.dominicum.ft.pd.mao.MakeParticipantMAO;
import eu.dominicum.ft.pd.mao.MakeStageResultDetailMAO;
import eu.dominicum.ft.pd.mao.MakeStageResultMAO;
import eu.dominicum.ft.pd.mao.validator.AssociateRiderWithTeamMemberMAO;

import javax.ejb.EJBException;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.validation.ValidationException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

/**
 * An EJB implementation of the {@link FantasyTourManagement} interface.
 *
 * @author Harry Klerks
 * @since 2013M1
 */
@Stateless
@TransactionAttribute (TransactionAttributeType.REQUIRES_NEW)
public class FantasyTourManagementEJB implements FantasyTourManagement {

    /**
     * The entity manager.
     */
    @PersistenceContext (name = "pu_ft")
    private EntityManager entityManager;

    @Override
    public List<FantasyTour> findAllFantasyTours() {

        Query namedQuery = entityManager.createNamedQuery("FantasyTour.findAll");
        return (List<FantasyTour>) namedQuery.getResultList();
    }

    @Override
    public FantasyTour makeFantasyTour(final MakeFantasyTourMAO mao) {

        try {
            FantasyTour newFantasyTour = FantasyTour.make(mao);
            this.entityManager.persist(newFantasyTour);
            return newFantasyTour;
        } catch (ValidationException vex) {
            throw new EJBException(vex.getMessage());
        }
    }

    @Override
    public FantasyTour findById(final Long fantasyTourId) {

        return this.entityManager.find(FantasyTour.class, fantasyTourId);
    }

    @Override
    public Participant makeParticipant(final MakeParticipantMAO makeParticipantMAO) {

        try {
            FantasyTour fantasyTour = this.entityManager.find(FantasyTour.class, makeParticipantMAO.getFantasyTourId());
            makeParticipantMAO.setFantasyTour(fantasyTour);
            if (makeParticipantMAO.getPersonId() != 0L) {
                makeParticipantMAO.setPerson(this.entityManager.find(Person.class, makeParticipantMAO.getPersonId()));
            }
            Participant newParticipant = fantasyTour.makeAndAddParticpant(makeParticipantMAO);
            this.entityManager.persist(fantasyTour);

            return newParticipant;
        } catch (ValidationException vex) {
            throw new EJBException(vex.getMessage());
        }

    }

    @Override
    public List<TeamMember> findAllTeamMembers(final Long tourId) {

        TypedQuery<TeamMember> query =
                this.entityManager.createNamedQuery("TeamMember.findByTour", TeamMember.class);
        query.setParameter("tourId", tourId);
        return query.getResultList();
    }

    @Override
    public void associateTeamMemberByYerseyNumber(final AssociateTeamMemberByYerseyNumberMAO associateTeamMemberByYerseyNumberMAO) {

        try {
            FantasyTour fantasyTour = this.entityManager.find(
                    FantasyTour.class, associateTeamMemberByYerseyNumberMAO.getFantasyTourId());
            fantasyTour.associateTeamMemberByYerseyNumber(associateTeamMemberByYerseyNumberMAO);
            this.entityManager.merge(fantasyTour);
        } catch (IllegalArgumentException iaex) {
            throw new EJBException(iaex);
        }
    }

    @Override
    public StageResult makeStageResult(final MakeStageResultMAO makeStageResultMAO) {

        StageResult newStageResult = null;
        try {
            FantasyTour fantasyTour = this.entityManager.find(FantasyTour.class, makeStageResultMAO.getFantasyTourId());
            Stage stage = fantasyTour.getTour().findStageById(makeStageResultMAO.getStageId());
            if (makeStageResultMAO.getLocationId() != null) {
                Location location = this.entityManager.find(Location.class, makeStageResultMAO.getLocationId());
                makeStageResultMAO.setLocation(location);
            }
            if (makeStageResultMAO.getMakeLocationMAO().getCountryId() != null) {
                Country country = this.entityManager.find(Country.class, makeStageResultMAO.getMakeLocationMAO().getCountryId());
                makeStageResultMAO.getMakeLocationMAO().setCountry(country);
            }
            makeStageResultMAO.setStage(stage);
            for (MakeStageResultDetailMAO makeStageResultDetailMAO : makeStageResultMAO.getMakeStageResultDetailsMAOs()) {
                Integer yerseyNumber = makeStageResultDetailMAO.getYerseyNumber();
                TeamMember teamMember = fantasyTour.getTour().findTeamMemberByYerseyNumber(yerseyNumber);
                if (teamMember != null) {
                    makeStageResultDetailMAO.setTeamMember(teamMember);
                } else {
                    throw new EJBException("Rugnummer " + yerseyNumber + " is ongeldig");
                }
            }
            newStageResult = stage.makeAndAddStageResult(makeStageResultMAO);
            this.entityManager.merge(fantasyTour);
            return newStageResult;
        } catch (ValidationException vex) {
            throw new EJBException(vex.getMessage());
        }

    }

    @Override
    public void calculateScoresForStage(final Long fantasyStageId) {

        TypedQuery typedQuery =
                this.entityManager.createNamedQuery("FantasyScore.findByFantasyStage", FantasyScore.class);
        typedQuery.setParameter("fantasyStageId", fantasyStageId);
        List<FantasyScore> existingFantasyScores = typedQuery.getResultList();

        for (FantasyScore existingFantasyScore : existingFantasyScores) {
            this.entityManager.remove(existingFantasyScore);
        }

        FantasyStage fantasyStage = this.entityManager.find(FantasyStage.class, fantasyStageId);
        List<FantasyScore> fantasyScores = fantasyStage.calculateScores();
        for (FantasyScore fantasyScore : fantasyScores) {
            this.entityManager.persist(fantasyScore);
        }
    }

    @Override
    public Vector calculateOverallRankings(final Long fantasyTourId) {

        Query query = this.entityManager.createNamedQuery("FantasyScore.calculateTotalsByParticipant");
        query.setParameter("fantasyTourId", fantasyTourId);
        query.setParameter("stageResultType", EStageResultType.OVERALL);
        return (Vector) query.getResultList();
    }

    @Override
    public Vector calculatePointsRankings(final Long fantasyTourId) {

        Query query = this.entityManager.createNamedQuery("FantasyScore.calculateTotalsByParticipant");
        query.setParameter("fantasyTourId", fantasyTourId);
        query.setParameter("stageResultType", EStageResultType.POINTS);
        return (Vector) query.getResultList();
    }

    @Override
    public Vector calculateClimbingRankings(final Long fantasyTourId) {

        Query query = this.entityManager.createNamedQuery("FantasyScore.calculateTotalsByParticipant");
        query.setParameter("fantasyTourId", fantasyTourId);
        query.setParameter("stageResultType", EStageResultType.CLIMBING);
        return (Vector) query.getResultList();
    }

    @Override
    public Vector calculateYouthRankings(final Long fantasyTourId) {

        Query query = this.entityManager.createNamedQuery("FantasyScore.calculateTotalsByParticipant");
        query.setParameter("fantasyTourId", fantasyTourId);
        query.setParameter("stageResultType", EStageResultType.YOUTH);
        return (Vector) query.getResultList();
    }

    @Override
    public void revealParticpantRealName(final Long fantasyTourId, final Long participantId) {

        FantasyTour fantasyTour = this.entityManager.find(FantasyTour.class, fantasyTourId);
        fantasyTour.revealParticipantRealName(participantId);
        this.entityManager.merge(fantasyTour);
    }

    @Override
    public void associateRiderWithTeamMember(final AssociateRiderWithTeamMemberMAO associateRiderWithTeamMemberMAO) {

        FantasyTour fantasyTour = this.entityManager.find(FantasyTour.class,
                associateRiderWithTeamMemberMAO.getFantasyTourId());

        ProfessionalCyclist rider = null;
        if (associateRiderWithTeamMemberMAO.getProfessionalCyclistId() != null) {
            rider = this.entityManager.find(ProfessionalCyclist.class,
                    associateRiderWithTeamMemberMAO.getProfessionalCyclistId());
        }
        associateRiderWithTeamMemberMAO.getAssociateRiderMAO().setRider(rider);
        fantasyTour.associateRiderWithTeamMember(associateRiderWithTeamMemberMAO);

        this.entityManager.merge(fantasyTour);
    }

    @Override
    public void associateLocationsWithStage(final AssociateLocationsWithStageMAO associateLocationsWithStageMAO) {

        FantasyTour fantasyTour = this.entityManager.find(FantasyTour.class, associateLocationsWithStageMAO.getFantasyTourId());
        if (associateLocationsWithStageMAO.getToLocationId() != null) {
            associateLocationsWithStageMAO.setToLocation(this.entityManager.find(Location.class, associateLocationsWithStageMAO.getToLocationId()));
        }
        if (associateLocationsWithStageMAO.getFromLocationId() != null) {
            associateLocationsWithStageMAO.setFromLocation(this.entityManager.find(Location.class, associateLocationsWithStageMAO.getFromLocationId()));
        }
        fantasyTour.associateLocationsWithStage(associateLocationsWithStageMAO);
        this.entityManager.merge(fantasyTour);
    }

    @Override
    public Vector calculateTotalRankings(final Long selectedFantasyTour, final Long filterStage) {

        Query query;
        if (filterStage == null || filterStage.equals(0L)) {
            query = this.entityManager.createNamedQuery("FantasyScore.calculateGrandTotals");
            query.setParameter("fantasyTourId", selectedFantasyTour);
        } else {
            query = this.entityManager.createNamedQuery("FantasyScore.calculateGrandTotalsByStage");
            query.setParameter("fantasyTourId", selectedFantasyTour);
            query.setParameter("stageId", filterStage);
        }
        return (Vector) query.getResultList();
    }

    @Override
    public void calculateIdealTeam(final Long selectedFantasyTour, final Long participantId) {

        FantasyTour fantasyTour = this.findById(selectedFantasyTour);
        Map<TeamMember, Map<Integer, Integer>> teamMemberMap = new HashMap<>();

        for (Team team : fantasyTour.getTour().getTeams()) {
            for (TeamMember teamMember : team.getTeamMembers()) {
                for (Stage stage : fantasyTour.getTour().getStages()) {
                    for (StageResult stageResult : stage.getStageResults()) {
                        for (StageResultDetail stageResultDetail : stageResult.getStageResultDetails()) {
                            if (stageResultDetail.getTeamMember().equals(teamMember)) {
                                if (!teamMemberMap.containsKey(teamMember)) {
                                    Map<Integer, Integer> rankAndPointsMap = new HashMap<>();
                                    for (int x = 1;x < 16; x++) {
                                        rankAndPointsMap.put(x, 0);
                                    }
                                    teamMemberMap.put(teamMember, rankAndPointsMap);
                                }
                                switch (stageResult.getResultType()) {
                                    case CLIMBING: {
                                        for (int x = 1; x < 16; x++) {
                                            teamMemberMap.get(teamMember).put(x, teamMemberMap.get(teamMember).get(x) + stageResultDetail.getPoints());
                                        }
                                    }
                                    case OVERALL:{
                                        for (int x = 1; x < 16; x++) {
                                            teamMemberMap.get(teamMember).put(x, teamMemberMap.get(teamMember).get(x) + (16 - x));
                                        }
                                    }
                                    case POINTS: {
                                        for (int x = 1; x < 16; x++) {
                                            teamMemberMap.get(teamMember).put(x, teamMemberMap.get(teamMember).get(x) + stageResultDetail.getPoints());
                                        }
                                    }
                                    case YOUTH: {
                                        for (int x = 1; x < 16; x++) {
                                            teamMemberMap.get(teamMember).put(x, teamMemberMap.get(teamMember).get(x) + (16 - x));
                                        }
                                    }
                                    default:
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    @Override
    public String toString() {

        StringBuilder stringBuilder = new StringBuilder("FantasyTourManagementEJB{");
        stringBuilder.append("entityManager=");
        String em = entityManager.toString();
        stringBuilder.append(em);
        stringBuilder.append("}");

        return stringBuilder.toString();
    }
}
